package com.ebo.synframework.nettybase.codec;

import com.google.protobuf.GeneratedMessageV3;
import org.apache.log4j.Logger;
import org.jboss.netty.buffer.ChannelBuffer;
import org.jboss.netty.channel.Channel;
import org.jboss.netty.channel.ChannelHandlerContext;
import org.jboss.netty.handler.codec.oneone.OneToOneDecoder;

import com.ebo.synframework.nettybase.dispatcher.ReqCmd;
import com.ebo.synframework.nettybase.protoPool.ProtoBuilderPoolManager;
import com.ebo.synframework.nettybase.protoPool.ReusedProtoBuilder;
import com.ebo.synframework.nettybase.protoPool.ReusedProtoBuilderType;
import com.ebo.synframework.util.CryptUtil;
import com.google.protobuf.GeneratedMessage;
import com.google.protobuf.InvalidProtocolBufferException;
import com.google.protobuf.Message;
import com.google.protobuf.Message.Builder;

public class ProtoDecoder extends OneToOneDecoder {

	private static Logger logger = Logger.getLogger("synroom");
	
	private final LengthOptimizeConfig lengthOptimizeConfig;
	
	public ProtoDecoder() {
		this.lengthOptimizeConfig = new LengthOptimizeConfig();
	}
	
	public ProtoDecoder(LengthOptimizeConfig lengthOptimizeConfig) {
		this.lengthOptimizeConfig = lengthOptimizeConfig;
	}

	@Override
    protected Object decode(ChannelHandlerContext ctx, Channel channel, Object msg) throws Exception {
        if (!(msg instanceof ChannelBuffer)) {
            return msg;
        }
        ChannelBuffer buf = (ChannelBuffer) msg;
        if (buf.hasArray()) {
        	return decode0(ctx, buf);
        } else {
            logger.warn("receive msg meet a null buf");
        }
        return null;
    }

	/**
	 *  &nbsp&nbsp cmdIndex(4)<br/>
	 * + serviceAndMethodLength(1)<br/>
	 * + serviceAndMethodStr(serviceAndMethodLength)<br/>
	 * + userIdAndsessionKeyStrLength(1)<br/>
	 * + userIdAndsessionKeyStr(userIdAndsessionKeyStrLength)<br/>
	 * + protoNameLength(1)<br/>
	 * + protoNameStr(protoNameLength)<br/>
	 * + protoData(endAll...)<br/>
	 * @param ctx
	 * @param buf
	 * @return
	 * @throws InvalidProtocolBufferException
	 */
	public ReqCmd decode0(ChannelHandlerContext ctx, ChannelBuffer buf)
			throws InvalidProtocolBufferException {
		try {
			int length = buf.readableBytes();
			int cmdIndex = buf.readInt();
			int serviceAndMethodLength = buf.readByte();
			ChannelBuffer serviceAndMethodBuffer = buf.readBytes(serviceAndMethodLength);
			String serviceAndMethodStr = new String(serviceAndMethodBuffer.array());
			String actServiceAndMethodName = this.lengthOptimizeConfig.getDecoderServiceNameMap().get(serviceAndMethodStr);
			if(actServiceAndMethodName != null) {
				serviceAndMethodStr = actServiceAndMethodName;
			}
			int userIdAndsessionKeyStrLength = buf.readByte();
			String userIdAndsessionKeyStr = null;
			if(userIdAndsessionKeyStrLength != 0) {
				ChannelBuffer userIdAndsessionKeyStrBuffer = buf.readBytes(userIdAndsessionKeyStrLength);
				userIdAndsessionKeyStr = new String(userIdAndsessionKeyStrBuffer.array()); 
			}
			int protoLength = buf.readByte();
			ChannelBuffer protoBuffer = buf.readBytes(protoLength);
			String protoNameStr = new String(protoBuffer.array()); 
			String actProtoName = this.lengthOptimizeConfig.getDecoderProtoNameMap().get(protoNameStr);
			if(actProtoName != null) {
				protoNameStr = actProtoName;
			}
			byte[] dst = new byte[buf.readableBytes()];
			buf.readBytes(dst);
//			if(ctx != null && ctx.getAttachment() != null && dst.length > 0) {
//				dst = CryptUtil.decryptByAes(dst, (String)ctx.getAttachment(), 128);
//			}
			ReqCmd cmd = null;
			if(ReusedProtoBuilderType.containType(protoNameStr)){
				ReusedProtoBuilder builder = ProtoBuilderPoolManager.getBuilder(ProtoFactory.getProtoInstance(protoNameStr).getClass());
				builder.getBuilder().mergeFrom(dst);
				cmd = new ReqCmd(cmdIndex, serviceAndMethodStr, builder, userIdAndsessionKeyStr, length);
			}else{
				GeneratedMessageV3 proto = (GeneratedMessageV3) ProtoFactory.getProtoInstance(protoNameStr).getParserForType().parseFrom(dst);
				cmd = new ReqCmd(cmdIndex, serviceAndMethodStr, proto, userIdAndsessionKeyStr, length);
			}
			return cmd;
		} catch(Exception e) {
			e.printStackTrace();
		}
		return null;
	}

}
